/*
 * Copyright Nucleron R&D LLC 2016
 *
 * This file is licensed under the terms of YAPL,
 * see License.txt for details.
 */

#include <stdbool.h>
#include <hwboard.h>
// #include <libopencm3/stm32/rcc.h>
// #include <libopencm3/stm32/gpio.h>
// #include <libopencm3/stm32/timer.h>

// #include <libopencm3/cm3/cortex.h>
// #include <libopencm3/cm3/nvic.h>
// #include <libopencm3/cm3/scb.h>

#include <plc_config.h>
#include <plc_abi.h>
#include <plc_wait_tmr.h>
#include <plc_hw.h>
#include <plc_iom.h>
#include <plc_dbg.h>
unsigned char plc_state = Empty;
bool plc_dbg_mode =false;

bool plc_dbg_jmpr_get(void)
{
    board.get_dbg_jmpr_get();
    return false;
}

volatile bool reset_jmp = false;

bool plc_rst_jmpr_get(void)
{
    ///Write your code here!!!
    return board.get_rst_jmpr_get();
}

void plc_boot_init(void)
{
}


void plc_boot_mode_enter(void)
{
    uint32_t delay;
    //Enter dbg mode if not there
    plc_dbg_mode = true;
    //Set boot pin
    // digitalWrite(PLC_BOOT_PERIPH, LOW);
    //Wait boot pin voltage to set
    // PLC_CLEAR_TIMER(delay);
    // while (PLC_TIMER(delay) < 2000)
    vTaskDelay(1000);
    ;
    //May reset the system now
    HAL_NVIC_SystemReset();
}

bool plc_get_din(uint32_t i)
{
    return board.get_din(i);
}

void plc_set_dout(uint32_t i, bool val)
{
    board.set_dout(i, val);
}

uint32_t plc_get_ain(uint32_t i)
{
    (void)i;
    return 0;
}

void plc_set_aout(uint32_t i, uint32_t val)
{
    (void)i;
    (void)val;
}

#include "dbnc_flt.h"
dbnc_flt_t in_flt[16];

//Digital io
#define LOCAL_PROTO dio
void PLC_IOM_LOCAL_INIT(void)
{
    board.begin();
    for (int i = 0; i < 16; i++)
    {
        dbnc_flt_init(in_flt + i);
    }
}

bool PLC_IOM_LOCAL_TEST_HW(void)
{
    return true;
}

const char plc_dio_err_sz[] = "Wrong variable size!";

const char plc_dio_err_asz[] = "Digital I/O address must be one number!";
const char plc_dio_err_asz_flt[] = "Digital input filter address must consist of two numbers!";

const char plc_dio_err_tp_flt[] = "Digital input filter must have memory type!";

const char plc_dio_err_ilim[] = "Digital input must have address 1...8!";
const char plc_dio_err_flt_lim[] = "Digital input must have 0 or 1 address!";
const char plc_dio_err_olim[] = "Digital output must have address 1...4!";

bool PLC_IOM_LOCAL_CHECK(uint16_t i)
{
    return true;
    uint32_t addr;
    switch (PLC_APP->l_tab[i]->v_size)
    {
    case PLC_LSZ_X:
        if (2 != PLC_APP->l_tab[i]->a_size)
        {
            // plc_curr_app->log_msg_post(LOG_CRITICAL, (char *)plc_dio_err_asz, sizeof(plc_dio_err_asz));
            return false;
        }

        addr = PLC_APP->l_tab[i]->a_data[1];
        //Check type and address
        switch (PLC_APP->l_tab[i]->v_type)
        {
        case PLC_LT_I:
            if (addr < 1 || addr > 8)
            {
                // plc_curr_app->log_msg_post(LOG_CRITICAL, (char *)plc_dio_err_ilim, sizeof(plc_dio_err_ilim));
                return false;
            }
            else
            {
                return true;
            }

        case PLC_LT_M:
        default:
            // plc_curr_app->log_msg_post(LOG_CRITICAL, (char *)plc_dio_err_sz, sizeof(plc_dio_err_sz));
            return false;

        case PLC_LT_Q:
            if (addr < 1 || addr > 4)
            {
                // plc_curr_app->log_msg_post(LOG_CRITICAL, (char *)plc_dio_err_olim, sizeof(plc_dio_err_olim));
                return false;
            }
            else
            {
                return true;
            }
        }
        return true;

    case PLC_LSZ_B:
        if (2 != PLC_APP->l_tab[i]->a_size)
        {
            // plc_curr_app->log_msg_post(LOG_CRITICAL, (char *)plc_dio_err_asz_flt, sizeof(plc_dio_err_asz_flt));
            return false;
        }

        if (PLC_LT_M != PLC_APP->l_tab[i]->v_type)
        {
            // plc_curr_app->log_msg_post(LOG_CRITICAL, (char *)plc_dio_err_tp_flt, sizeof(plc_dio_err_tp_flt));
            return false;
        }

        addr = PLC_APP->l_tab[i]->a_data[1];

        if (addr < 1 || addr > 8)
        {
            // plc_curr_app->log_msg_post(LOG_CRITICAL, (char *)plc_dio_err_ilim, sizeof(plc_dio_err_ilim));
            return false;
        }

        addr = PLC_APP->l_tab[i]->a_data[1];
        if (addr > 1)
        {
            // plc_curr_app->log_msg_post(LOG_CRITICAL, (char *)plc_dio_err_flt_lim, sizeof(plc_dio_err_flt_lim));
            return false;
        }
        return true;

    default:
        // plc_curr_app->log_msg_post(LOG_CRITICAL, (char *)plc_dio_err_sz, sizeof(plc_dio_err_sz));
        return false;
    }
}

void PLC_IOM_LOCAL_BEGIN(uint16_t lid)
{
}
void PLC_IOM_LOCAL_END(uint16_t lid)
{
}
void PLC_IOM_LOCAL_START(void)
{
}
void PLC_IOM_LOCAL_STOP(void)
{
}

uint32_t PLC_IOM_LOCAL_SCHED(uint16_t lid, uint32_t tick)
{
    return 0;
}
void PLC_IOM_LOCAL_POLL(uint32_t tick)
{
    uint32_t i;
    for (i = 0; i < 8; i++)
    {
        dbnc_flt_poll(in_flt + i, tick, plc_get_din(i));
    }
    board.input();
}
uint32_t PLC_IOM_LOCAL_WEIGTH(uint16_t lid)
{
    return PLC_APP->l_tab[lid]->a_data[1];
}
uint32_t PLC_IOM_LOCAL_GET(uint16_t i)
{
    switch (plc_curr_app->l_tab[i]->v_type)
    {
    case PLC_LT_I:
    case PLC_LT_M:
        // *(bool *)(plc_curr_app->l_tab[i]->v_buf) = dbnc_flt_get(in_flt + plc_curr_app->l_tab[i]->a_data[1] - 1);
        board.get(plc_curr_app->l_tab[i]->a_data[0], plc_curr_app->l_tab[i]->a_data[1], plc_curr_app->l_tab[i]->v_buf);
        break;

        // case PLC_LT_M:
        //     if (plc_curr_app->l_tab[i]->a_data[1])
        //     {
        //         *(uint8_t *)(plc_curr_app->l_tab[i]->v_buf) = (uint8_t)in_flt[plc_curr_app->l_tab[i]->a_data[1] - 1].thr_on;
        //     }
        //     else
        //     {
        //         *(uint8_t *)(plc_curr_app->l_tab[i]->v_buf) = (uint8_t)in_flt[plc_curr_app->l_tab[i]->a_data[1] - 1].thr_off;
        //     }
        //     break;

    default:
        break;
    }
    return 0;
}
uint32_t PLC_IOM_LOCAL_SET(uint16_t i)
{
    switch (plc_curr_app->l_tab[i]->v_type)
    {
    case PLC_LT_M:
    case PLC_LT_Q:
        board.set(plc_curr_app->l_tab[i]->a_data[0], plc_curr_app->l_tab[i]->a_data[1], (void *)(plc_curr_app->l_tab[i]->v_buf));
        break;

        // case PLC_LT_M:
        //     if (plc_curr_app->l_tab[i]->a_data[1])
        //     {
        //         in_flt[plc_curr_app->l_tab[i]->a_data[1] - 1].thr_on = (uint32_t) * (uint8_t *)(plc_curr_app->l_tab[i]->v_buf);
        //     }
        //     else
        //     {
        //         in_flt[plc_curr_app->l_tab[i]->a_data[1] - 1].thr_off = (uint32_t) * (uint8_t *)(plc_curr_app->l_tab[i]->v_buf);
        //     }
        //     break;
    }
    return 0;
}
#undef LOCAL_PROTO

PLC_IOM_METH_DECLS(plc_diag);

const plc_io_metods_t plc_iom_registry[] =
    {
        PLC_IOM_RECORD(plc_diag),
        PLC_IOM_RECORD(dio),
};
//Must be declared after plc_iom_registry
PLC_IOM_REG_SZ_DECL;

uint8_t mid_from_pid(uint16_t proto)
{
    switch (proto)
    {
    case 0:
        return 0;
    case 1:
        return 1;
    default:
        return 0;
    }
    return PLC_IOM_MID_ERROR;
}
// void setReboot()
// {
//     // plc_app_stop();
//     // plc_app_start();
//     plc_curr_app->config_init();
// }
void setReboot()
{
    vTaskDelay(1000);
    HAL_NVIC_SystemReset();
}
